import sys
import os
import re

import Image
import map_load_helper as mlh
import gg

for infile in sys.argv[1:]:
    print infile
    try:
        im = Image.open(infile).convert('1')
        print 'pix 00:', im.getpixel((0,0))
        im.save(infile)
    except IOError:
        pass

# status consts
OK = 0
WARN1 = 1
WARN2 = 2
ERR = 3

# levels validation
# consts stating desired values
collision_pixmode = '1'
posfixes = {
    'col': '.collision.png',
    'view': '.jpg',
    'actors': '.precrop.svgz'
    }

rpt_stage_template = {
    'pre': None,
    'status': OK, # see status consts for other values
    'lax': [], # prefixes d(.) vs desired dd(.)
    'missing_files': [],
    'pixformat conversion': False,
    'sizes': {}, # put file_type: im.size) pairs for each relevant file
    'size_mismatch': False,
    'missing_actors': [], # ref rect plus any required by game mode
    'redefined_actors': [], # multiple instances for actor allowed only one
    'out_of_bounds_actors': []
    }
rpt_ladder_template = {
    'status': OK, # see status consts for other values
    'no_levels': False,
    'holes': [],
    'no_stage0': False,
    'broken_stages': []
    }

def update_status(rpt, st):
    rpt['status']=max(rpt['status'], st)

def load_img(fname):
    im = Image.open(fname)
    return im


#re.match(rx, string) is None -> no match
basedir = os.path.join('..','data','levels')
rx = re.compile(r'(\d{1,2})\.') # matchs d. or dd. , group 1 gives d or dd

def rpt_all(basedir):
    for gm in ['sp','coop','sxs', 'steal']:
        all_rpt = {}
        all_rpt[gm] = rpt_ladder(basedir, gm)

def rpt_ladder(basedir, game_mode):
        rpt = dict(rpt_ladder_template)
        prefixes = set()
        sdir = os.path.join(basedir, game_mode)
        shorts = [ f for f in os.listdir(sdir)]
        # build a set of valid prefixes 
        for s in shorts:
            match = rx.match(s)
            if match is not None:
                pre = match.group(1)
                if os.path.isfile(os.path.join(sdir, s)):
                    prefixes.add(pre)

        # no levels
        if len(prefixes)==0:
            rpt['no_levels'] = True
            update_status(rpt, ERR)
            return rpt

        # stage 0 and holes
        li = [ int(e) for e in list(prefixes)]
        min_stage = min(li)
        max_stage = max(li)
        if min_stage!=0:
            rpt['no_stage0']=True
            update_status(rpt,ERR)            
            for i in xrange(min_stage):
                rpt['holes'].append(i)
        for i in xrange(min_stage, max_stage+1):
            if '%02d'%i not in prefixes:
                update_status(rpt, ERR)
                rpt['holes'].append(i)

        # report lax prefixes ( d. vs the desired dd.)
        rpt['lax'] = [ pre for pre in prefixes if len(pre)!=2]
        if len(rpt['lax']):
            update_status(rpt,ERR)

        # run test on individual levels
        for pre in prefixes:
            rpt_stage = dict(rpt_stage_template)
            rpt_stage['pre'] = pre
            ok_prefix(pre, rpt_stage)
            ok_collision(sdir, pre, rpt_stage)
            ok_view(sdir, pre, rpt_stage)
            ok_actors(sdir, game_mode, pre, rpt_stage)
            ok_uniform_sizes(rpt_stage)
            stage_status = rpt_stage['status']
            update_status(rpt, stage_status)
            if stage_status != OK:
                rpt['broken_stages'].append(rpt_stage)
        return rpt

def ok_prefix(pre, rpt):
    try:
        int(pre)
        ok = True
    except ValueError:
        ok = False
    ok = ok and len(pre)==2
    if not ok:
        rpt['bad_prefix'] = pre
        update_status(rpt, ERR)
    return ok

def ok_collision(sdir, pre, rpt):
    rpt['pixformat conversion'] = False
    short = pre + posfixes['col']
    fname = os.path.join(sdir, short)
    if os.path.exists(fname):
        im = load_img(fname)
        # store img size for further checks
        rpt['sizes']['col'] = im.size
        # check img mode
        if im.mode!=collision_pixmode:
            # risky, but we do autoconvert anyway
            update_status(rpt, WARN1)
            rpt['pixformat conversion'] = True
            im.convert(collision_pixmode)
            im.save(fname)
    else:
        rpt['missing_files'].append(fname)
        update_status(rpt, ERR)
        
def ok_view(sdir, pre, rpt):
    short = pre + posfixes['view']
    fname = os.path.join(sdir, short)
    if os.path.exists(fname):
        im = load_img(fname)
        # store img size for further checks
        rpt['sizes']['view'] = im.size
    else:
        rpt['missing_files'].append(fname)
        update_status(rpt, ERR)
    
def ok_actors(sdir, game_mode, pre, rpt):
    short = pre + posfixes['actors']
    fname = os.path.join(sdir, short)
    if os.path.exists(fname):
        #? refactor to a func in map_loader_helper --> 
        actors_by_colors = mlh.get_actors(fname, gg.mapactors_to_color4['terrain'])
        # transcode the table actors by color to actor by mapactor name
        mapactors = {}
        for m_name in gg.mapactors_to_color4:
            #print '\tm_name:',m_name
            color4 = gg.mapactors_to_color4[m_name]
            try:
                mapactors[m_name] = actors_by_colors[color4]
            except KeyError:
                print '\t no actor with type:', m_name
                # the map dont have this actor type
                pass 
        del actors_by_colors
        #? <-- refactor to a func in map_loader_helper

        ok_actors_internal(game_mode, mapactors, rpt)
    else:
        rpt['missing_files'].append(fname)
        update_status(rpt, ERR)
    

def ok_actors_internal(game_mode, mapactors, rpt):
    # side effect: must build rpt['sizes']['actors'] with terrain size
    # seria preferible que los tests fuesen no destructivos, asi model puede
    # llamarlo
    def unique(mactor):#, mapactor, rpt):
        ok = mactor in mapactors
        if ok:
            group = mapactors[mactor]
            ok = len(group)==1 
            if not ok:
                rpt['redefined_actors'].append(mactor)
                update_status(rpt, ERR)
        else:
            rpt['missing_actors'].append(mactor)
            update_status(rpt, ERR)
        return ok

    def at_least_one(mactor):
        ok = mactor in mapactors
        if not ok:
            rpt['missing_actors'].append(mactor)
            update_status(rpt, ERR)
        return ok

    def goal_for_player(mplayer):
        numchr = mplayer[-1]
        own = 'goal_zone_player' + numchr
        shared = 'goal_zone_dual'
        ok = (own in mapactors) or (shared in mapactors)
        if not ok:
            rpt['missing_actors'].append('goal for %s'%mplayer)
            update_status(rpt, ERR)            
        return ok
        
    # terrain
    ok_terrain = unique('terrain')
    if ok_terrain:
        pos, width, height = mapactors['terrain'][0]
        rpt['sizes']['actors'] = (width, height)

    # presence player(s), related pigeons, related goal zones
    unique('bird_player1')
    at_least_one('pigeon_player1')
    goal_for_player('bird_player1')
    if game_mode != 'sp':
        unique('bird_player2')
        at_least_one('pigeon_player2')
        goal_for_player('bird_player2')
    
    # actors pos into terrain rect
    if ok_terrain:
        width, height = rpt['sizes']['actors']
        for mactor in mapactors:
            group = mapactors[mactor]
            for pos, _, _ in group:
                x = pos.real
                y = pos.imag
                if x<0.0 or x>width or y<0.0 or y>height:
                    rpt['out_of_bounds_actors'].append(('%s at %d %d')%(
                                                        mactor, int(x), int(y)))
                    update_status(rpt, WARN1)            
                    

def ok_uniform_sizes(rpt):
    sizes = rpt['sizes']
    if len(sizes)==0:
        return
    it = sizes.itervalues()
    size0 = it.next()
    equals = True
    for siz in it:
         equals = equals and (siz==size0)
    rpt['size_mismatch'] = not equals
    if not equals:
        update_status(rpt, ERR)

    

def test_actors():
    game_mode = 'sp'
    rpt = rpt_ladder(basedir, game_mode)
    import pprint
    pprint.pprint(rpt)
    

# for distribution other files (and subdirs) must be removed

if __name__ == '__main__':
    test_actors()    
    
